home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS.Lib & Samples / =How to write your app < prev    next >
Encoding:
Text File  |  1992-10-22  |  32.0 KB  |  764 lines  |  [TEXT/MPS ]

  1. The title of this document should have been:
  2.  
  3. How to write your application using Zippo.
  4.  
  5. The above is too long for a file name, but obviously the title I used got your
  6. attention.  Now all I have to do is to hold it...
  7.  
  8. If you have built the libraries and sample applications, you may have already
  9. noted that Zippo is actually an executable application.  Of course, it does
  10. absolutely nothing useful.  That's your job.
  11.  
  12. First, let me state what you have built.  If you use MPW, you built the library
  13. DTS.Lib.  If you are a Think user, you built the libraries DTS.Lib..framework,
  14. DTS.Lib..strings, and DTS.Lib..utils.
  15.  
  16. Think limits a library to a single 32k segment, so the library code had to be
  17. broken up into separate libraries for Think users.  MPW allows as big a library
  18. as you want, so it is all in one piece.  Either way, you only link in the code
  19. that you call, so both are efficient.
  20.  
  21. For Think C users, DTS.Lib..strings.π contains only a single source file,
  22. StringUtils.c.  StringUtils.c contains some useful string functions.  It allows
  23. you to do various functions that are supplied by sprintf.  The amount of code
  24. you have to link in when you choose sprintf is quite large, however.  The
  25. supplied string functions allow you to easily format and parse string data
  26. without linking a large chunk of code.
  27.  
  28. It is suggested that you link the StringUtils executable into the code segment
  29. that contains main().  This will guarantee that the string functions are always
  30. resident in memory when you call them.  If the code isn't in ram when you make
  31. a string call, the loading of the code may cause memory to move or compress.
  32. This is fine, unless you passed a pointer into an unlocked handle as a string
  33. pointer.  If you point into an unlocked handle, simply calling the function can
  34. move memory, unless it is guaranteed that the code is already in memory.  By
  35. linking it into the same code segment as main(), you guarantee that it is
  36. always in memory.  (All of the sample applications already do this.)
  37.  
  38. For Think C users, DTS.Lib..utils.π contains the following source files:
  39.     AppleTalk.c
  40.     GWLayers.c
  41.     ListControl.c
  42.     PPC.c
  43.     TextEditControl.c
  44.     Utilities.c
  45.  
  46. Again, if you are using MPW, all of the files are linked into a single library
  47. called DTS.Lib.  The StringUtils code #pragma segment compiler option declares
  48. the code segment to be StringUtils.  The make files for the applications
  49. redirect StringUtils code to be placed in the Main segment, so you are guaranteed
  50. to have the StringUtils code always available in ram.
  51.  
  52.  
  53. The above code can be used without committing to the entire DTS.Lib application
  54. framework.  You can write your own application completely from scratch and then
  55. call any of the code in the above-mentioned files anytime you want.
  56.  
  57. Each of the above files has useful stand-alone code you will probably want to
  58. look at, even if you choose not to use the DTS.Lib application framework.
  59.  
  60. If you choose to use the DTS.Lib application framework, (from now on called
  61. DTS.framework), very much of the application development tasks are already done
  62. for you.
  63.  
  64. DTS.framework currently supports:
  65.     Multiple windows
  66.     Multiple document types
  67.     File I/O
  68.     Apple Events
  69.     Window/document scrolling
  70.     Hierarchical document architecture
  71.     Infinite undo (undos can optionally be saved with the document)
  72.     Floating palettes
  73.     Viewing window for displaying document objects
  74.     Other document-specific tasks, such as mouseDowns, keyDowns, cursors, etc.
  75.  
  76. What tasks DTS.framework doesn't directly supply can probably be found in the
  77. DTS utilities, so between the two, many of the aspects of Mac application
  78. development are already complete.
  79.  
  80.  
  81. So what is the minimum you have to know to get started?
  82.  
  83. Above, we discussed the various components of the DTS library code.  So now we
  84. need to know the easiest way to use this code.  The easiest way is to make a
  85. copy of the sample application Zippo, and start adding code.  Consider Zippo
  86. an application shell which already correctly uses the DTS.framework and
  87. utilities.  All that is missing is the code that is specific to your
  88. application.  Everything that is generic is already in there.
  89.  
  90. The best place to start explaining is how to manage documents.  We will start
  91. with a code snippet from Zippo:
  92.  
  93. switch (menuItem) {
  94.     case iNew:
  95.         err = NewDocument(&frHndl, docFileType, true);
  96.         if (!err) {
  97.             err = DoNewWindow(frHndl, nil, FrontWindow(),
  98.                               (WindowPtr)-1, kwAppWindow);
  99.             if (err) DisposeDocument(frHndl);
  100.         }
  101.         if (err) CenteredAlert(rErrorAlert, nil, (ModalFilterProcPtr)AlertFilter);
  102.         break;
  103.     case iOpen:
  104.         .
  105.         .
  106.         .
  107.  
  108.  
  109. This is right out of the file menu.c  We get to this code by the user choosing
  110. "New" from the file menu.  The first thing we do is to try to create the new
  111. document.  This is done with the DTS.framework call NewDocument().
  112.  
  113. All NewDocument() does, if successful, is to create a handle to the size of the
  114. document structure, and initialize many of the fields.  If it succeeds, frHndl
  115. is set to that handle.  If it fails, frHndl is set to nil, and the reason for
  116. the failure is returned into err.
  117.  
  118. As stated above, DTS.framework can support multiple document types.  The type
  119. of the document we are trying to create is docFileType, which is an OSType.
  120. In Zippo, docFileType is defined as follows:
  121.     #define docFileType      'DUMD'
  122.  
  123. 'DUMD' stands for dumb document.  I consider it dumb because when the document
  124. is saved, it is saved with no content.  The DTS.framework very carefully saves
  125. nothing.  You can then open nothing later.  You can print nothing, etc.
  126. Hopefully, you will remedy this situation.
  127.  
  128. The third parameter to NewDocument() tells NewDocument() that you want to
  129. create a document with a new "Untitled #" style of window title.  The true
  130. indicates to DTS.framework that you want this document to have an untitled
  131. number one greater then the last time that NewDocument() was called.  No big
  132. deal, but I figured you would curious at this point.
  133.  
  134. If NewDocument() succeeds, you now have a new document that has no window.
  135. Once the document is successfully create, you then want to give the document
  136. a window.  This is done with the DoNewWindow() call.  You pass in the frHndl
  137. that NewDocument() created.
  138.  
  139. The prototype for DoNewWindow is:
  140.  
  141. OSErr    DoNewWindow(FileRecHndl frHndl, WindowPtr *retWindow,
  142.                     WindowPtr relatedWindow, WindowPtr behind, short attributes)
  143.  
  144. If DoNewWindow() successfully creates a window, the window is returned in
  145. retWindow, if retWindow is not nil.  Many times you don't really care what the
  146. window that was created is.  In this case, if it is successful, it is now the
  147. new top window.  If the user clicks on it, then you care.  Here you don't.
  148.  
  149. If DoNewWindow() was unsuccessful at creating the window, you now are left with
  150. a document, frHndl, that has no window.  If this occurs, you want to dispose of
  151. the document, and report an error.
  152.  
  153. That's all there is to it.  Oh, the remaining parameters to DoNewWindow():
  154.     relatedWindow:    Create the window on the same monitor that holds most of
  155.                     this window. If nil is passed in, create the window on the
  156.                     main monitor.
  157.     behind:            Create the window behind the window indicated.
  158.                     -1 puts it on top.
  159.     attributes:        Various window attributes can be indicated here.  They are:
  160.  
  161.         #define kwGrowIcon            1
  162.         #define kwHScroll            2
  163.         #define kwHScrollLessGrow    6
  164.         #define kwVScroll            8
  165.         #define kwVScrollLessGrow    24
  166.         #define kwVisible            32
  167.         #define kwOpenAtOldLoc        64
  168.         #define kwDoFirstClick        128
  169.         #define kwIsDocument        0
  170.         #define kwIsPalette            256
  171.         #define kwIsModalDialog        512
  172.  
  173. So, if you want to create a window with document scrollbars, you just set a few
  174. bits in the attribute field, and you're done.  And yes, kwOpenAtOldLoc
  175. automatically opens the window wherever the user had it when it was closed,
  176. and yes it makes sure that it is at least partially visible on some monitor.
  177. You can find more information elsewhere.
  178.  
  179.  
  180. Now that we understand document creation and window creation using
  181. DTS.framework, here's the rest of the story:
  182.  
  183.  
  184. As mentioned above, NewDocument() returns a handle.  This handle is your
  185. reference to the document, thus my favorite variable name for one os these
  186. things:  frHndl (file reference handle).  You don't have to worry about
  187. associating the frHndl with the window that was created for it.  DoNewWindow()
  188. stores the frHndl in the window's refcon field.  Also, you don't have to worry
  189. about which window belongs to a particular frHndl, as the window is stored in
  190. the frHndl, as well.  The document and its associated window both point to one
  191. another.  (You got one, you got the other -- cool.)
  192.  
  193.  
  194. Basically, everything that goes on with document goes on inside the frHndl.
  195. The frHndl is a handle that holds the structure for the document.  Most of this
  196. structure looks like this:
  197.  
  198. typedef struct {
  199.     OSType                        sfType;
  200.     Boolean                        defaultDoc;
  201.     Movie                        movie;
  202.     short                        movieResID;
  203.     short                        movieFlags;
  204.     Boolean                        movieDataRefWasChanged;
  205.     Boolean                        docDirty;
  206.     long                        modNum;
  207.     long                        modTick;
  208.     Boolean                        readOnly;
  209.     short                        refNum;
  210.     short                        resRefNum;
  211.     FSSpec                        fss;
  212.     short                        windowID;
  213.     WindowPtr                    window;
  214.     GetDocWindowProcPtr            getDocWindow;
  215.     CalcFrameRgnProcPtr            calcFrameRgnProc;
  216.     ContentClickProcPtr            contentClickProc;
  217.     ContentKeyProcPtr            contentKeyProc;
  218.     DrawFrameProcPtr            drawFrameProc;
  219.     FreeDocumentProcPtr            freeDocumentProc;
  220.     FreeWindowProcPtr            freeWindowProc;
  221.     ImageProcPtr                imageProc;
  222.     InitContentProcPtr            initContentProc;
  223.     ReadDocumentProcPtr            readDocumentProc;
  224.     ReadDocumentHeaderProcPtr    readDocumentHeaderProc;
  225.     ResizeContentProcPtr        resizeContentProc;
  226.     ScrollFrameProcPtr            scrollFrameProc;
  227.     UndoFixupProcPtr            undoFixupProc;
  228.     WindowCursorProcPtr            windowCursorProc;
  229.     WriteDocumentProcPtr        writeDocumentProc;
  230.     WriteDocumentHeaderProcPtr    writeDocumentHeaderProc;
  231.     short                        attributes;
  232.     Rect                        windowSizeBounds;
  233.     ControlHandle                hScroll;
  234.     ControlHandle                vScroll;
  235.     short                        hScrollIndent;
  236.     short                        vScrollIndent;
  237.     short                        leftSidebar;
  238.     short                        topSidebar;
  239.     short                        hArrowVal;
  240.     short                        vArrowVal;
  241.     short                        hPageVal;
  242.     short                        vPageVal;
  243. } FileStateRec;
  244.  
  245.  
  246. Here's some explanation of some of the fields:
  247.  
  248. sfType:                    The type of the document you passed to NewDocument() is
  249.                         stored here.
  250.  
  251. defaultDoc:                Used by application framework to determine if View Hierarchy
  252.                         facility can be used to view the document.
  253.  
  254. movie:                    These 4 fields are used together.  If the document type used
  255. movieResID:                is of type 'MooV' for either NewDocument or OpenDocument,
  256. movieFlags:                then these fields are used to keep track of the movie information
  257. movieDataRefWasChanged:    for the document.  In addition to this information, the meaning of
  258.                         the field refNum is extended somewhat.  Here's the deal:
  259.                         Movie files are generally resource-fork-based, but this isn't
  260.                         always the case.  The movie file can be flattened and made to
  261.                         be data-fork-based.  This is so MS-DOS machines can handle the
  262.                         movie file.
  263.                         To be consistent with regular document files, the resRefNum
  264.                         returned by OpenMovieFile (which isn't always a resource refNum)
  265.                         is kept in the field refNum.  Normally refNum represents a data
  266.                         fork, but in the case of movies, it may be either a data fork or
  267.                         resource fork reference number.
  268.                         For regular documents, if you wish to use the resource fork, you
  269.                         use the calls UseDocResFile and CloseDocResFile.  These still work
  270.                         for movies, whether the movie is data-fork- or resource-fork-based.
  271.                         If the movie is resource fork based, then UseDocResFile simply
  272.                         copies the refNum field into the resRefNum field.  This is the
  273.                         correct thing to do, as the resource is already opened for the movie.
  274.                         CloseDocResFile looks at the refNum and resRefNum fields.  If they
  275.                         are the same, then it simply sets resRefNum to kInvalidRefNum.
  276.                         This is all that is necessary to "close" the resource fork, as you
  277.                         really don't want it to close, since it was opened for the movie.
  278.                         All of this behavior is simply to allow the various calls to do the
  279.                         appropriate thing when the document is a movie.  You should be able
  280.                         to handle movie files just like regular document files.
  281.  
  282. docDirty:                Used by the application framework to determine if a
  283.                         "Save before closing" dialog should be displayed.  
  284.  
  285. modNum:                    This is incremented whenever you call SetDocDirty().  Some
  286.                         applications find this information useful.  Most don't care.
  287.  
  288. modTick:                Tick count of last modification.  (See modNum).
  289.  
  290. readOnly:                True if document is opened read-only.  DTS.framework sets
  291.                         this if the document that is selected by the user can't be
  292.                         openeded with read-write access.  DTS.framework asks the
  293.                         user if it is okay to open it read-only.
  294.  
  295. refNum:                    The file reference number (0 if it is a new document.)
  296.  
  297. resRefNum:                Reference number of the resource fork of the document file.
  298.                         Unless you specifically use the resource fork, the
  299.                         document's resource fork isn't opened.
  300.  
  301. FSSpec                    The FSSpec for the open file (if there is one open for this
  302.                         document.)
  303.  
  304. windowID:                The resource ID of the 'WIND' resource to be used when
  305.                         DoNewDocument() is called to give this document a window.
  306.                         windowID is initialized to rWindow, which is defined to be
  307.                         128.  If you want a different 'WIND' resource for this
  308.                         document, set this field after calling NewDocument(), and
  309.                         prior to calling DoNewWindow().
  310.  
  311. window:                    When you call DoNewWindow(), if it succeeds at creating a
  312.                         window, it places the window pointer here.
  313.  
  314. The next fields are the procedure pointers that determine the behavior of the
  315. document.  These fields are initialized to point to the following functions:
  316.  
  317. getDocWindow:                GetStaggeredWindow;
  318. calcFrameRgnProc:            CalcFrameRgn;
  319. contentClickProc:            ContentClick;
  320. contentKeyProc:                ContentKey;
  321. drawFrameProc:                DrawFrame;
  322. freeDocumentProc:            FreeDocument;
  323. freeWindowProc:                FreeWindow;
  324. imageProc:                    ImageDocument;
  325. initContentProc:            InitContent;
  326. readDocumentProc:            ReadDocument;
  327. readDocumentHeaderProc:        DefaultReadDocumentHeader;
  328. resizeContentProc:            ResizeContent;
  329. scrollFrameProc:            ScrollFrame;
  330. undoFixupProc:                UndoFixup;
  331. windowCursorProc:            WindowCursor;
  332. writeDocumentProc:            WriteDocument;
  333. writeDocumentHeaderProc:    DefaultWriteDocumentHeader;
  334.  
  335. With the exception of the function GetStaggeredWindow, all of the above
  336. functions are part of the application shell Zippo.  GetStaggeredWindow is a
  337. function in the DTS utilities that opens a window "staggered" from existing
  338. windows.  This allows some of the window to be exposed so that the user can
  339. easily click on the window and bring it to the front.
  340.  
  341. All of the other functions are part of your application.  They get called by
  342. the application framework at appropriate times.  They don't get called at some
  343. magic moment.  They get called when your application calls DTS.framework to do
  344. something.  DTS.framework does the generic work. The application-specific work
  345. it can't do, and so it calls your application to do that part.
  346.  
  347. Even for something as supposedly application-specific as drawing the window
  348. content, you still will call DTS.framework to get the job started.  You will
  349. call the function DoImageDocument().  You pass DoImageDocument() a file
  350. reference handle (frHndl).  DoImageDocument() dereferences the frHndl and
  351. gets the value at the field imageProc.  If imageProc is nil, then there is
  352. no imaging procedure for this document, and DoImageDocument() just returns.
  353. If there is a procedure pointer stored in the field imageProc, then that
  354. code is called.
  355.  
  356. There is no particular magic happening here.  The DoImageDocument() code looks
  357. just as you would expect:
  358.  
  359. OSErr    DoImageDocument(FileRecHndl frHndl)
  360. {
  361.     ImageProcPtr    proc;
  362.     OSErr            err;
  363.  
  364.     err = noErr;
  365.     if (proc = (*frHndl)->fileState.imageProc) err = (*proc)(frHndl);
  366.     return(err);
  367. }
  368.  
  369. If there is an imaging procedure in the file reference, call it.  Otherwise
  370. it just returns noErr.
  371.  
  372. Initially imageProc has a value.  The initial value is ImageDocument.  The
  373. ImageDocument() function is located in the Zippo file Window2.c.  If your
  374. application only has a single document type, then just place your document
  375. imaging code inside the stub function ImageDocument().  It's that easy.
  376.  
  377. For the most part, all of the procedure pointers in the frHndl have a "Do"
  378. function.  For example:  If you want to draw the "frame" portion of a window,
  379. you wouldn't call DrawFrame().  You would call DoDrawFrame().  DoDrawFrame()
  380. will look up the procedure pointer, and if it is not nil, it will call it,
  381. just like DoImageDocument() does.
  382.  
  383.  
  384. So how exactly does a file reference handle get created?  Again, nothing
  385. magic, but there are a number of steps that need to be understood.
  386.  
  387.  
  388. The creation of an frHndl is invoked by the application.  The application
  389. calls either NewDocument() or OpenDocument(), discussed above.  OpenDocument()
  390. calls NewDocument(), so in either case, you are actually calling NewDocument().
  391.  
  392. I think the easiest way to explain what NewDocument() does is to show the code.
  393. The explanation follows.  Here's the code:
  394.  
  395.  
  396. OSErr    NewDocument(FileRecHndl *returnHndl, OSType sftype, Boolean incTitleNum)
  397. {
  398.     long            size;
  399.     FileRecHndl        frHndl;
  400.     FileRecPtr        frPtr;
  401.     Str255            untitled;
  402.     StringPtr        pstr;
  403.     OSErr            err;
  404.     short            i;
  405.     static short    untitledCount;
  406.  
  407.     err  = memFullErr;                /* Assume that we will fail. */
  408.  
  409.     size = InitDocumentSize(sftype);
  410.         /* Call the application and ask it how big the frHndl should be for
  411.         ** this document type.  We can't know, so we'll ask. */
  412.  
  413.     if (*returnHndl = frHndl = (FileRecHndl)NewHandleClear(size)) {
  414.         /* Create (or try to) the frHndl, initialized to all 0's */
  415.  
  416.         for (i = gTypeListLen; --i;) if (sftype == gTypeList[i]) break;
  417.             /* Walk the typeList to find this file type.  We are interested in
  418.             ** where we find the entry.  The position we find it is used as a
  419.             ** string number into the rDefaultTitles STR# resource.  We get
  420.             ** an individual string from this location.  This allows us to
  421.             ** have different default titles for different document types. */
  422.  
  423.         for (++i; i; i--) {
  424.             GetIndString(untitled, rDefaultTitles, i);
  425.             if (untitled[0]) break;  /* Quit if we succeeded at getting one. */
  426.         }
  427.         frPtr = *frHndl;
  428.         frPtr->fileState.sfType                  = sftype;
  429.         frPtr->fileState.modNum                  = GetModNum();
  430.         frPtr->fileState.modTick                 = TickCount();
  431.         frPtr->fileState.refNum                  = kInvalRefNum;
  432.         frPtr->fileState.resRefNum               = kInvalRefNum;
  433.         frPtr->fileState.fss.vRefNum             = kInvalVRefNum;
  434.         frPtr->fileState.windowID                = rWindow;
  435.             /* The above sets the fileState constants for the document.  Note
  436.             ** that we use a default 'WIND' ID for the expected window resource.
  437.             ** This can be changed later, if the default isn't good enough. */
  438.  
  439.         frPtr->fileState.getDocWindow            = GetStaggeredWindow;
  440.         frPtr->fileState.calcFrameRgnProc        = CalcFrameRgn;
  441.         frPtr->fileState.contentClickProc        = ContentClick;
  442.         frPtr->fileState.contentKeyProc          = ContentKey;
  443.         frPtr->fileState.drawFrameProc           = DrawFrame;
  444.         frPtr->fileState.freeDocumentProc        = FreeDocument;
  445.         frPtr->fileState.freeWindowProc          = FreeWindow;
  446.         frPtr->fileState.imageProc               = ImageDocument;
  447.         frPtr->fileState.initContentProc         = InitContent;
  448.         frPtr->fileState.readDocumentProc        = ReadDocument;
  449.         frPtr->fileState.readDocumentHeaderProc  = DefaultReadDocumentHeader;
  450.         frPtr->fileState.resizeContentProc       = ResizeContent;
  451.         frPtr->fileState.scrollFrameProc         = ScrollFrame;
  452.         frPtr->fileState.undoFixupProc           = UndoFixup;
  453.         frPtr->fileState.windowCursorProc        = WindowCursor;
  454.         frPtr->fileState.writeDocumentProc       = WriteDocument;
  455.         frPtr->fileState.writeDocumentHeaderProc = DefaultWriteDocumentHeader;
  456.             /* Initialize all of the procedure pointers to default values.
  457.             ** If the defaults aren't any good, they will be changed later. */
  458.  
  459.         pstr = frPtr->fileState.fss.name;
  460.         pcpy(pstr, untitled);
  461.         if (incTitleNum) ++untitledCount;
  462.         pcatdec(pstr, untitledCount);
  463.             /* Create the default document title.  It is stored in the FSSpec,
  464.             ** as we can't place it in the window title.  We don't have a window
  465.             ** yet to "title".  This will happen later.  The title is gotten
  466.             ** from the FSSpec, so we are effectively done. */
  467.  
  468.         err = InitDocument(frHndl);
  469.             /* Call the application for any additional document initialization.
  470.             ** Other handles may need to be allocated.  The default values
  471.             ** above may be incorrect for a certain document type.  This gives
  472.             ** the application a chance to change any defaults that
  473.             ** are incorrect. */
  474.  
  475.         if (err) {
  476.             DisposeHandle((Handle)frHndl);
  477.             *returnHndl = nil;
  478.                 /* If the application couldn't complete the document
  479.                 ** initialization, pitch the handle. */
  480.         }
  481.     }
  482.  
  483.     return(err);
  484. }
  485.  
  486.  
  487.  
  488. Above is the entire NewDocument() function.  There are two key points to note:
  489.  
  490. 1) The call to InitDocumentSize()
  491. 2) The call to InitDocument()
  492.  
  493. These two functions are part of your application.  (They are in the source file
  494. file2.c)  The first is called just so NewDocument() knows how big a handle to
  495. create for the document reference.  If your application has only one document
  496. type, then this function will simply return a constant.  If your application
  497. has multiple document types, you will want to return the size that corresponds
  498. to the document type (OSType).
  499.  
  500. Your application's function InitDocumentSize() is passed the OSType.  At this
  501. point, this is the only piece of information we have on the document-to-be.
  502. Until the application framework knows the size, it can't create the file
  503. reference handle.  You tell the application framework how big to create the
  504. frHndl.  It creates it to the requested size, and initializes it to 0's and
  505. default values.
  506.  
  507. Once it is initialized to defaults, NewDocument() calls your application, giving
  508. it a chance to change the default values.  Only the frHndl is passed to
  509. InitDocument().  The frHndl from this point on is the document reference.
  510.  
  511. If you have different document types, you will have to have a reasonably smart
  512. InitDocument() function.  It will have to look at the document type, and then
  513. adjust the default values for the frHndl based on the document type.  At this
  514. point, the document type is stored in the frHndl, in the field fileState.sfType.
  515.  
  516. All of this is done prior to a window being created for the frHndl.  This is
  517. important, as a number of the procedure pointers determine how the window will
  518. be created.  You need to make customizations to the frHndl after its creation,
  519. and prior to the creation of the document's window.
  520.  
  521. Of course, if your application only has one document type, then these defaults
  522. are just fine, and you won't have to worry about all of this stuff.
  523.  
  524.  
  525. Let's revisit the code for a "New" menu item choice:
  526.  
  527.  
  528. err = NewDocument(&frHndl, docFileType, true);
  529. if (!err) {
  530.     err = DoNewWindow(frHndl, nil, FrontWindow(), (WindowPtr)-1, kwAppWindow);
  531.     if (err) DisposeDocument(frHndl);
  532. }
  533. if (err) CenteredAlert(rErrorAlert, nil, (ModalFilterProcPtr)AlertFilter);
  534.  
  535.  
  536. We have explored what happens when NewDocument() is called.  All that remains
  537. is the DoNewWindow() call.
  538.  
  539.  
  540. A window is opened for the frHndl.  The values stored in the frHndl are used
  541. to determine how to open the window.
  542.  
  543. In the sample call above, we create a window for the document frHndl.  Here's
  544. what the parameters indicate:
  545.  
  546. nil:            Don't bother returning a reference to the window created.
  547.                 If we cared, we might pass in something like &newWindow.
  548. FrontWindow():    Window is created on the same monitor that holds most of the
  549.                 current front window.  
  550. (WindowPtr)-1:    Window is created as the front-most window.
  551. kwAppWindow:    Window is created with these attributes (described above).
  552.  
  553.  
  554. That's all there is to it.  Now all you need is a way to hold your document
  555. data.
  556.  
  557.  
  558. Interestingly enough, DTS.framework has plenty of document support.  You can
  559. use the hierarchical document architecture built into DTS.framework.  It is
  560. very powerful.  It handles file I/O, hierarchically related data, infinite
  561. undo, etc.
  562.  
  563.  
  564. The hierarchical document architecture is the default type of document.  If
  565. you choose to use it, there are already calls supplied to create the initial
  566. default document.  All you have to do is to call DefaultInitDocument() in
  567. your application's InitDocument() function.  For all documents that use the
  568. hierarchical document architecture, simply call DefaultInitDocument() from
  569. within InitDocument().
  570.  
  571. What DefaultInitDocument does is it creates a root object of type TreeObjHndl
  572. and places it in an expected location within the frHndl for the document.
  573. Consider this as a document within a document.  The frHndl describes the
  574. run-time aspects of the document.  The root object in the hierarchy begins
  575. the portion of the document that "persists", that is, saves to disk.
  576.  
  577. The root object is just that.  Yes, it has a data portion to it, but the main
  578. purpose of the root object is a holder of the "child" objects of the document.
  579.  
  580. Whenever you make a change to the document, you will make it to some number of
  581. objects with the hierarchy.  There are specific calls for this, and if you stick
  582. to these calls, infinite undo works automatically.  There is a separate document
  583. describing the hierarchical document architecture.  You will find it in the
  584. DTS.Lib folder.  It is called "=Using TreeObj.c".  It will detail everything
  585. you need to know about using the hierarchical document package.
  586.  
  587. Let's review the chain of information for a document, startiong with the window:
  588.  
  589. window:        References the document (frHndl) associated with the window.
  590.             The reference to the frHndl is kept in the window's refcon field.
  591.  
  592. frHndl:        References back to the window.  This means that if you have either
  593.             the window or the frHndl for a document, you can easily get the
  594.             other.
  595.  
  596.             Also references the root hierarchy object.  This is true only if you
  597.             elect to use the TreeObj package in the DTS.framework.  DTS.Draw
  598.             uses this package.  MacShell does not.
  599.  
  600.  
  601. root:        References the frHndl that contains it.
  602.  
  603.             Also references the undo root object that contains all of the
  604.             information necessary to undo/redo edits to the document.
  605.  
  606.             References all of the children added to the root object.
  607.  
  608.  
  609. child:        References all child it may in turn have.
  610.  
  611.             References the parent object.  The root object can also be
  612.             considered a child, but it is a child with no parent.  This
  613.             is reflected in the parent reference.  If the parent reference
  614.             is nil, then the object is a root object.
  615.  
  616.  
  617. This is a nice top-down view of where document/window information is stored.
  618. Here's some more information:
  619.  
  620. frHndl information is NOT saved with the document.  Any information you wish
  621. to be saved with the document must be contained in a hierarchy object.
  622.  
  623. Changes to the root object can't be undone.  The location in the document
  624. that is automatically kept for undo/redo operations is referenced by parent
  625. and child number.  This locates you to any object within the hierarchy,
  626. except for one.  The root object has no parent, so the location of the root
  627. object can not be indicated by parent/childNum.
  628.  
  629. This means:
  630.  
  631. • Any information tht is run-time, and you DON'T want saved to disk should be
  632.   kept directly in the frHndl.
  633. • Any information that you want to keep with the document, but you don't want
  634.   involved in undo/redo operations should be kept in the root object.
  635. • Any information that you want to be able to save and undo/redo in the
  636.   document should be kept in a child below the level of the root object.
  637.  
  638.  
  639. Of course, all of the rules from the root object down imply that you are using
  640. the hierarchical document architecture.  This is a completely optional package.
  641. All other rules continue to apply, whether or not you are using this package.
  642.  
  643.  
  644.  
  645.  
  646. With the exception of the root object, all of the above information is kept it
  647. the fileState portion of an frHndl.  The root object begins the actual document,
  648. so it is actually in the document portion of an frHndl.  Why am I talking about
  649. frHndl portions?  Because an frHndl is comprised of three distinct structures.
  650. They are:
  651.  
  652. FileStateRec:    This is most adequately covered above.
  653. ConnectRec:        This hasn't even been mentioned.
  654. TheDoc:            This has been hinted at.
  655.  
  656.  
  657. The ConnectRec portion of a document record looks like this:
  658.  
  659. typedef struct {
  660.     long            windowID[2];        /* Used to match up windows. */
  661.     short            endSendInfo;        /* Above is send info.         */
  662.  
  663.     Boolean            connected;            /* Flag showing we are connected.      */
  664.     AEAddressDesc    remoteLoc;            /* AppleEvent address of remote user. */
  665.     Str32            remoteName;            /* Name of user connected to.          */
  666.     Str32            remoteZone;            /* Zone of user connected to.          */
  667.     Str32            remoteMachine;        /* Machine name of user connected to. */
  668.     short            endLocalInfo;        /* Above info is for 1 machine only.  */
  669. } ConnectRec;
  670.  
  671. The ConnectRec is used by DTS.framework to establish a connection to
  672. another application (or itself).  This connection links two specific windows.
  673. Once connected, the boolean 'connected' is set true, and the other fields
  674. are filled in.  The AEAddressDesc of whom you connected to is saved.  The name,
  675. zone, and machine name are also kept.  The endLocalInfo field is a reference
  676. point to determine the end of the ConnectRec at run-time.  This is here in case
  677. there are additional fields added in the future.  They would be added before the
  678. endLocalInfo field.  Since the ConnectRec structure is DTS.framework's
  679. responsibility to maintain, you don't have to worry about this.  (I do.)
  680.  
  681.  
  682. To see how to use the Apple Event facilities of DTS.framework, check out the
  683. application MacShell.  It uses these facilities.
  684.  
  685.  
  686.  
  687. The final component of an frHndl is the document portion.  It looks like this:
  688.  
  689.  
  690. typedef struct {
  691.     DocHeaderInfo    fhInfo;  /* Doc hdr info (vers, prRec, window loc.) */
  692.     TreeObjHndl        root;
  693. } TheDoc;
  694.  
  695.  
  696. where DocHeaderInfo looks like this:
  697.  
  698.  
  699. typedef struct {
  700.     short        version;            /* The file format version.                 */
  701.     Boolean        printRecValid;        /* True if prRec has been created.         */
  702.     TPrint        print;                /* Print record for file.                 */
  703.     Rect        structureRect;        /* Remember where window was when saved. */
  704.     Rect        contentRect;        /* Remember where window was when saved. */
  705.     Rect        stdState;            /* This and below rect used for saving     */
  706.     Rect        userState;            /* zoom information for window.             */
  707.     long        hDocSize;            /* hDocSize and vDocSize have to be      */
  708.     long        vDocSize;            /* saved with the document /* so that     */
  709.                                     /* the window can be created the         */
  710.                                     /* correct size.                         */
  711.     short        endDocHeaderInfo;    /* End version, print, and window info.     */
  712. } DocHeaderInfo;
  713.  
  714.  
  715. I suppose you figured that the document portion was your domain.  Well, it is,
  716. kind of.  Since DTS.framework supplies hierarchical document support, it has
  717. to have access to this portion of the frHndl, as well.  You always need the
  718. fhInfo field of the document portion.  Also, it must be the first field in
  719. the structure.
  720.  
  721. You optionally need the root field.  If you are using the hierarchical document
  722. package, then you must have this field, and it must be the second field.
  723.  
  724. Aside from these two rules, the structure is all yours.  You can add as much as
  725. you wish to it.
  726.  
  727.  
  728. As has been described above, a single frHndl consists of three components.
  729. These are:
  730.     1) FileStateRec
  731.     2) ConnectRec
  732.     3) The document record.
  733.  
  734. The way you assemble these parts into a single frHndl is like this:
  735.  
  736.  
  737. typedef struct FileRec {
  738.     FileStateRec    fileState;        /* DTS.Lib expects this structure here. */
  739.     ConnectRec        connect;        /* DTS.Lib expects this structure here. */
  740.     union {
  741.         TheDoc    doc;                /* Union in each document type here. */
  742.     } d;
  743. } FileRec;
  744.  
  745.  
  746. This combines the three documents parts into a single structure.  Note the use
  747. of union for the actual document portion.  This is because you can have multiple
  748. document types within your application.  Whenever you add a document type, just
  749. create a structure for it, and then union in this new structure into the FileRec
  750. definition.  That's all there is to it (sort of).
  751.  
  752. You have to remember to change the InitDocumentSize() and InitDocument()
  753. functions so that they can handle the new document type.  Once this is done,
  754. you now have a new document type added to your application.
  755.  
  756.  
  757. I think this should give you a good feel of what you need to first, second, etc.
  758. Any other questions about using DTS.Lib and Zippo should be answered in other
  759. read.me files.  Of course, you can always look at the sample applications
  760. DTS.Draw and MacShell for implementation-specific details.
  761.  
  762.  
  763. Eric Soldan
  764.